package ru.voice2blog.engine;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.voice2blog.common.V2BService;
import ru.voice2blog.common.exception.InitException;
import ru.voice2blog.common.exception.PrepareException;
import ru.voice2blog.common.exception.PublishException;
import ru.voice2blog.common.exception.UploadException;
import ru.voice2blog.common.publisher.PublishResult;
import ru.voice2blog.common.publisher.Publisher;
import ru.voice2blog.common.uploader.DataUploader;
import ru.voice2blog.common.uploader.UploadResult;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

/**
 * User: ilya.ufo
 * Date: 22.05.11
 * Time: 2:03
 */
public class ModuleFactory {

    private static final Logger log = LoggerFactory.getLogger(ModuleFactory.class);

    private static final String STORE_MODULE_CLASS = "storeModuleClass";
    private static final String PUBLISH_MODULE_CLASS = "pulbishModuleClass";
    private static final String STORE_PROPERTIES_FILE_NAME = "storePropertiesFileName";
    private static final String PUBLISH_PROPERTIES_FILE_NAME = "publishPropertiesFileName";


    public static DataUploader getDataUploader(Properties properties) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InitException {
        String storeModuleName = properties.getProperty(STORE_MODULE_CLASS);
        log.info("Store module class name is " + storeModuleName);

        String storePropertiesFileName = properties.getProperty(STORE_PROPERTIES_FILE_NAME);

        return getModuleInstance(storePropertiesFileName, storeModuleName, DataUploader.class);
    }

    public static Publisher getPublisher(Properties properties) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InitException {
        String publishModuleName = properties.getProperty(PUBLISH_MODULE_CLASS);
        log.info("Publish module class name is " + publishModuleName);

        String publishPropertiesFileName = properties.getProperty(PUBLISH_PROPERTIES_FILE_NAME);

        return getModuleInstance(publishPropertiesFileName, publishModuleName, Publisher.class);
    }

    private static <T extends V2BService> T getModuleInstance(String propertyFileName, String moduleName, Class<T> clazzType) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InitException {
        T instance = Class.forName(moduleName).asSubclass(clazzType).newInstance();
//        if (instance instanceof Publisher) {
//            instance = (T) new PublisherWrapper((Publisher) instance);
//        } else {
//            instance = (T) new DataUploaderWrapper((DataUploader) instance);
//        }
        instance.init(loadProperties(propertyFileName));
        return instance;
    }

    public static Properties loadProperties(String propertiesName) {
        Properties mainProperties = new Properties();
        try {
            mainProperties.load(new FileInputStream(propertiesName));
            log.info("Property file {} read successfully.", propertiesName);
        } catch (IOException e) {
            log.error("Error on reading properties", e);
            System.exit(1);
        }
        return mainProperties;
    }

    enum ServiceState {
        NOT_INITED,
        INITED,
        PREPARED,
    }

    static class ServiceWrapper<T extends V2BService> implements V2BService {

        protected T original;
        protected ServiceState state = ServiceState.NOT_INITED;


        ServiceWrapper(T original) {
            this.original = original;
        }

        public void init(Properties properties) throws InitException {
            try {
                if (state != ServiceState.NOT_INITED) {
                    throw new IllegalStateException("Must be in not inited state");
                }
                original.init(properties);
                state = ServiceState.INITED;
            } catch (Exception e) {
                log.error("Error on init.", e);
            }
        }

        public void prepare(Properties propertySet) throws PrepareException {
            original.prepare(propertySet);
        }

        public void clean() {
            original.clean();
        }

        public void destroy() {
            original.destroy();
        }
    }

    static class DataUploaderWrapper extends ServiceWrapper<DataUploader> implements DataUploader {
        DataUploaderWrapper(DataUploader original) {
            super(original);
        }

        public UploadResult upload(File file) throws UploadException {
            return original.upload(file);
        }
    }

    static class PublisherWrapper extends ServiceWrapper<Publisher> implements Publisher {
        PublisherWrapper(Publisher original) {
            super(original);
        }

        public PublishResult publish(String soundUrl) throws PublishException {
            return original.publish(soundUrl);
        }
    }
}
